package core;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.UUID;

/**
 * 字符串处理工具（空值安全优化版）
 * <p>
 * 参考Hutool的StrUtil类实现，提供丰富的字符串处理方法。
 * </p>
 */
public final class StringUtil {

    /**
     * 字符串常量：空字符串 {@code ""}
     */
    public static final String EMPTY = "";

    /**
     * 字符串常量：空格符 {@code " "}
     */
    public static final String SPACE = " ";

    private static final Set<Integer> BLANK_CHAR_SET = Set.of(
            32, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8200,
            8201, 8202, 8239, 8287, 65279
    );

    private StringUtil() {
        throw new AssertionError("Utility class");
    }

    /**
     * 空字符串判断（空值安全）
     *
     * @param str 待检查字符串
     * @return 为null或空字符串时返回true
     */
    public static boolean isEmpty(CharSequence str) {
        return str == null || str.length() == 0;
    }

    /**
     * 非空字符串判断（空值安全）
     *
     * @param str 待检查字符串
     * @return 非空字符串时返回true
     */
    public static boolean isNotEmpty(CharSequence str) {
        return !isEmpty(str);
    }

    /**
     * 空白字符串判断（含不可见字符）
     *
     * @param str 待检查字符串
     * @return 空白字符串时返回true
     */
    public static boolean isBlank(CharSequence str) {
        if (isEmpty(str)) {
            return true;
        }

        for (int i = 0; i < str.length(); i++) {
            // 只要有一个非空字符即为非空字符串
            if (!isBlankChar(str.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    /**
     * 非空白字符串判断
     *
     * @param str 待检查字符串
     * @return 非空白字符串时返回true
     */
    public static boolean isNotBlank(CharSequence str) {
        return !isBlank(str);
    }

    /**
     * 安全截取字符串（空值安全）
     *
     * @param str        目标字符串
     * @param beginIndex 起始索引
     * @param endIndex   结束索引
     * @return 截取后的字符串（输入空时返回空字符串）
     */
    public static String safeSubstring(String str, int beginIndex, int endIndex) {
        if (isEmpty(str)) {
            return EMPTY;
        }
        int length = str.length();
        beginIndex = Math.max(0, beginIndex);
        endIndex = Math.min(length, Math.max(beginIndex, endIndex));
        return str.substring(beginIndex, endIndex);
    }

    /**
     * 安全连接字符串（空值安全）
     *
     * @param delimiter 分隔符
     * @param elements  元素数组
     * @return 连接后的字符串
     */
    public static String safeJoin(String delimiter, Object... elements) {
        if (ObjectUtil.isEmpty(elements)) {
            return EMPTY;
        }
        StringBuilder sb = new StringBuilder();
        for (Object element : elements) {
            if (element != null) {
                if (sb.length() > 0) {
                    sb.append(delimiter);
                }
                sb.append(element);
            }
        }
        return sb.toString();
    }

    /**
     * 字符空白检查
     */
    private static boolean isBlankChar(int codePoint) {
        return Character.isWhitespace(codePoint) || BLANK_CHAR_SET.contains(codePoint);
    }

    /**
     * 当给定字符串为null时，转换为Empty
     *
     * @param str 被检查的字符串
     * @return 原字符串或者空串
     */
    public static String emptyIfNull(CharSequence str) {
        return nullToEmpty(str);
    }

    /**
     * 当给定字符串为null时，转换为Empty
     *
     * @param str 被转换的字符串
     * @return 转换后的字符串
     */
    public static String nullToEmpty(CharSequence str) {
        return nullToDefault(str, EMPTY);
    }

    /**
     * 如果字符串是 {@code null}，则返回指定默认字符串，否则返回字符串本身。
     *
     * @param str        要转换的字符串
     * @param defaultStr 默认字符串
     * @return 字符串本身或指定的默认字符串
     */
    public static String nullToDefault(CharSequence str, String defaultStr) {
        return (str == null) ? defaultStr : str.toString();
    }

    /**
     * 如果字符串是{@code null}或者""，则返回指定默认字符串，否则返回字符串本身。
     *
     * @param str        要转换的字符串
     * @param defaultStr 默认字符串
     * @return 字符串本身或指定的默认字符串
     */
    public static String emptyToDefault(CharSequence str, String defaultStr) {
        return isEmpty(str) ? defaultStr : str.toString();
    }

    /**
     * 如果字符串是{@code null}或者""或者空白，则返回指定默认字符串，否则返回字符串本身。
     *
     * @param str        要转换的字符串
     * @param defaultStr 默认字符串
     * @return 字符串本身或指定的默认字符串
     */
    public static String blankToDefault(CharSequence str, String defaultStr) {
        return isBlank(str) ? defaultStr : str.toString();
    }

    /**
     * 当给定字符串为空字符串时，转换为{@code null}
     *
     * @param str 被转换的字符串
     * @return 转换后的字符串
     */
    public static String emptyToNull(CharSequence str) {
        return isEmpty(str) ? null : str.toString();
    }

    /**
     * 反转字符串<br>
     * 例如：abcd =》dcba
     *
     * @param str 被反转的字符串
     * @return 反转后的字符串
     */
    public static String reverse(String str) {
        if (str == null) {
            return null;
        }
        return new StringBuilder(str).reverse().toString();
    }

    /**
     * 将已有字符串填充为规定长度，如果已有字符串超过这个长度则返回这个字符串<br>
     * 字符填充于字符串前
     *
     * @param str        被填充的字符串
     * @param filledChar 填充的字符
     * @param len        填充长度
     * @return 填充后的字符串
     */
    public static String fillBefore(String str, char filledChar, int len) {
        return fill(str, filledChar, len, true);
    }

    /**
     * 将已有字符串填充为规定长度，如果已有字符串超过这个长度则返回这个字符串<br>
     * 字符填充于字符串后
     *
     * @param str        被填充的字符串
     * @param filledChar 填充的字符
     * @param len        填充长度
     * @return 填充后的字符串
     */
    public static String fillAfter(String str, char filledChar, int len) {
        return fill(str, filledChar, len, false);
    }

    /**
     * 将已有字符串填充为规定长度，如果已有字符串超过这个长度则返回这个字符串
     *
     * @param str        被填充的字符串
     * @param filledChar 填充的字符
     * @param len        填充长度
     * @param isPre      是否填充在前
     * @return 填充后的字符串
     */
    public static String fill(String str, char filledChar, int len, boolean isPre) {
        final int strLen = str.length();
        if (strLen > len) {
            return str;
        }

        String filledStr = repeat(filledChar, len - strLen);
        return isPre ? filledStr.concat(str) : str.concat(filledStr);
    }

    /**
     * 重复某个字符
     *
     * @param c     被重复的字符
     * @param count 重复次数
     * @return 重复后的字符串
     */
    public static String repeat(char c, int count) {
        if (count <= 0) {
            return EMPTY;
        }

        char[] result = new char[count];
        for (int i = 0; i < count; i++) {
            result[i] = c;
        }
        return new String(result);
    }

    /**
     * 生成随机UUID
     *
     * @return UUID字符串
     */
    public static String uuid() {
        return UUID.randomUUID().toString();
    }

    /**
     * 将对象转为字符串
     *
     * @param obj 对象
     * @return 字符串
     */
    public static String utf8Str(Object obj) {
        return str(obj, StandardCharsets.UTF_8);
    }

    /**
     * 将对象转为字符串
     *
     * @param obj     对象
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(Object obj, Charset charset) {
        if (null == obj) {
            return null;
        }

        if (obj instanceof String) {
            return (String) obj;
        } else if (obj instanceof byte[]) {
            return str((byte[]) obj, charset);
        } else if (obj instanceof Byte[]) {
            return str((Byte[]) obj, charset);
        } else if (obj instanceof ByteBuffer) {
            return str((ByteBuffer) obj, charset);
        }

        return obj.toString();
    }

    /**
     * 将byte数组转为字符串
     *
     * @param bytes   byte数组
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(byte[] bytes, Charset charset) {
        if (bytes == null) {
            return null;
        }

        if (null == charset) {
            return new String(bytes);
        }
        return new String(bytes, charset);
    }

    /**
     * 将Byte数组转为字符串
     *
     * @param bytes   Byte数组
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(Byte[] bytes, Charset charset) {
        if (bytes == null) {
            return null;
        }

        byte[] bs = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            bs[i] = bytes[i];
        }
        return str(bs, charset);
    }

    /**
     * 将字符串转为byte数组
     *
     * @param str     字符串
     * @param charset 字符集
     * @return byte数组
     */
    public static byte[] bytes(String str, Charset charset) {
        if (str == null) {
            return null;
        }
        if (charset == null) {
            return str.getBytes();
        }
        return str.getBytes(charset);
    }

    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data    数据
     * @param charset 字符集，如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, Charset charset) {
        if (null == charset) {
            charset = Charset.defaultCharset();
        }
        return charset.decode(data).toString();
    }

    /**
     * 截断字符串，使用其按照UTF-8编码为字节后不超过maxBytes长度。截断后自动追加省略号(...)
     * 用于存储数据库varchar且编码为UTF-8的字段
     *
     * @param str      java字符串
     * @param maxBytes 最大字节长度
     * @return 截断后的字符
     */
    public static String truncateUtf8(String str, int maxBytes) {
        if (isEmpty(str)) return EMPTY;

        byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
        if (bytes.length <= maxBytes) return str;

        // 精确计算截断位置
        int truncateIndex = maxBytes;
        while (truncateIndex > 0 && (bytes[truncateIndex] & 0xC0) == 0x80) {
            truncateIndex--;
        }

        return new String(bytes, 0, truncateIndex, StandardCharsets.UTF_8) + "...";
    }

    /**
     * 截断字符串，使用其按照指定编码为字节后不超过maxBytes长度<br>
     * 此方法用于截取总bytes数不超过指定长度，如果字符出没有超出原样输出，如果超出了，则截取掉超出部分，并可选添加...，
     * 但是添加"..."后总长度也不超过限制长度。
     *
     * @param str        原始字符串
     * @param charset    指定编码
     * @param maxBytes   最大字节数
     * @param factor     速算因子，取该编码下单个字符的最大可能字节数
     * @param appendDots 截断后是否追加省略号(...)
     * @return 截断后的字符串
     */
    public static String truncateByByteLength(String str, Charset charset, int maxBytes, int factor,
                                              boolean appendDots) {
        //字符数*速算因子<=最大字节数
        if (str == null || str.length() * factor <= maxBytes) {
            return str;
        }
        final byte[] sba = str.getBytes(charset);
        if (sba.length <= maxBytes) {
            return str;
        }
        //限制字节数
        final int limitBytes;
        if (appendDots) {
            limitBytes = maxBytes - "...".getBytes(charset).length;
        } else {
            limitBytes = maxBytes;
        }
        final ByteBuffer bb = ByteBuffer.wrap(sba, 0, limitBytes);
        final StringBuilder cb = new StringBuilder(limitBytes);
        final java.nio.CharBuffer charBuffer = java.nio.CharBuffer.allocate(limitBytes);
        final java.nio.charset.CharsetDecoder decoder = charset.newDecoder();
        //忽略被截断的字符
        decoder.onMalformedInput(java.nio.charset.CodingErrorAction.IGNORE);
        try {
            decoder.decode(bb, charBuffer, true);
            decoder.flush(charBuffer);
            cb.append(charBuffer.flip());
        } catch (Exception e) {
            // 忽略解码异常
        }
        final String result = cb.toString();
        if (appendDots) {
            return result + "...";
        }
        return result;
    }
}